
#include <BVectorArray>
#include <BCoordArray>
#include "member_BGeoSphere.h"

using namespace BWE;

member_BGeoSphere::member_BGeoSphere(BGeoSphere* geosphere)
{
	boss = geosphere;
	radius = 1.0f;
	divide = 0;
	dirty = true;
}
member_BGeoSphere::~member_BGeoSphere()
{

}

void member_BGeoSphere::divideVertexs(BVector* vs, int ia, int ib, int ic, int div)
{
	int iab = (ia + ib) >> 1;
	BVector cab = (vs[ia] + vs[ib]);
	cab.setLength(1.0f);

	int ibc = (ib + ic) >> 1;
	BVector cbc = vs[ib] + vs[ic];
	cbc.setLength(1.0f);

	int ica = (ic + ia) >> 1;
	BVector cca = vs[ic] + vs[ia];
	cca.setLength(1.0f);

	vs[iab] = cab;
	vs[ibc] = cbc;
	vs[ica] = cca;

	div--;
	if (div > 0)
	{
		divideVertexs(vs, ia, iab, ica, div);
		divideVertexs(vs, iab, ib, ibc, div);
		divideVertexs(vs, ica, ibc, ic, div);
		divideVertexs(vs, iab, ibc, ica, div);
	}
}

bool member_BGeoSphere::rebuild()
{
	BSpace space(-1, -1, -1, 1, 1, 1);
	if (divide < 0)
	{
		boss->clear();
		return false;
	}

	{
		BReal angleV = PIf / 6.0f;
		BReal z = sin(angleV);
		BReal angleH = PI2f / 5.0f;
		for (int i = 0; i < 5; i++)
		{
			BReal lenxy = cos(angleV);
			BReal angleA = angleH * i;
			BReal angleB = angleH * (i + (BReal)0.5);
			vecs[0 + i].set(cos(angleA) * lenxy, sin(angleA) * lenxy, z);
			vecs[5 + i].set(cos(angleB) * lenxy, sin(angleB) * lenxy, -z);
		}
		BVector a(0, 0, 1);
		BVector b(0, 0, -1);
		trigs[0].set(a, vecs[0], vecs[1]);
		trigs[1].set(a, vecs[1], vecs[2]);
		trigs[2].set(a, vecs[2], vecs[3]);
		trigs[3].set(a, vecs[3], vecs[4]);
		trigs[4].set(a, vecs[4], vecs[0]);
		for (int i = 0; i < 5; i++)
		{
			int i0 = i;
			int i1 = (i + 1) % 5;
			trigs[5 + i].set(vecs[i0], vecs[5 + i0], vecs[i1]);
			trigs[10 + i].set(vecs[5 + i0], vecs[5 + i1], vecs[i1]);
		}
		trigs[15].set(b, vecs[6], vecs[5]);
		trigs[16].set(b, vecs[7], vecs[6]);
		trigs[17].set(b, vecs[8], vecs[7]);
		trigs[18].set(b, vecs[9], vecs[8]);
		trigs[19].set(b, vecs[5], vecs[9]);
	}

	segment = (int)pow(2, divide);
	stride = (segment + 2) * (segment + 1) / 2;
	if (stride == 0)
		stride = 3;
	BVectorArray* vertices = new BVectorArray(stride * 20);
	if (segment > 1)
	{
		BVectorArray buffer((segment + 1) * (segment + 1));
		int ia = 0;
		int ib = (segment + 1) * segment;
		int ic = (segment + 1) * (segment + 1) - 1;
		for (int i = 0; i < 20; i++)
		{
			BVector* vs = vertices->data() + stride * i;
			buffer[ia] = trigs[i].a();
			buffer[ib] = trigs[i].b();
			buffer[ic] = trigs[i].c();
			divideVertexs(buffer.data(), ia, ib, ic, divide);
			int vi = 0;
			for (int r = 0; r <= segment; r++)
			{
				int begin = r * (segment + 1);
				for (int c = 0; c < r + 1; c++)
				{
					vs[vi++] = buffer[begin + c];
				}
			}
		}
	}
	else
	{
		for (int i = 0; i < 20; i++)
		{
			(*vertices)[i * 3 + 0] = trigs[i].a();
			(*vertices)[i * 3 + 1] = trigs[i].b();
			(*vertices)[i * 3 + 2] = trigs[i].c();
		}
	}

	BCoordArray* coords = new BCoordArray(stride * 20);
	for (int i = 0; i < 20; i++)
	{
		BCoord* cs = coords->data() + stride * i;
		BVector* vs = vertices->data() + stride * i;
		const BTriangle& trig = trigs[i];
		BSpace space(trig.a());
		space.expand(trig.b());
		space.expand(trig.c());
		if (space.max().x() > 0 && space.min().y() < 0 && space.max().y() >= 0)
		{
			for (int c = 0; c < stride; c++)
			{
				const BVector& v = vs[c];
				if (v.z() == 1.0f)
				{
					cs[c].set(0.5f, 0.0f);
					continue;
				}
				if (v.z() == -1.0f)
				{
					cs[c].set(0.5f, 1.0f);
					continue;
				}
				BVector vxy(v.x(), v.y(), 0);
				vxy.setLength(1);
				BReal angleH = vxy.angle(1, 0, 0);
				if (v.y() <= 0.0f)
					angleH = PI2f - angleH;
				else
					angleH = PI2f + angleH;
				BReal angleV = v.angle(0, 0, 1);
				cs[c].set(float(angleH / PI2f), float(angleV / PIf));
			}
		}
		else
		{
			for (int c = 0; c < stride; c++)
			{
				const BVector& v = vs[c];
				if (v.z() == 1.0f)
				{
					cs[c].set(0.5f, 0.0f);
					continue;
				}
				if (v.z() == -1.0f)
				{
					cs[c].set(0.5f, 1.0f);
					continue;
				}
				BVector vxy(v.x(), v.y(), 0);
				vxy.setLength(1);
				BReal angleH = vxy.angle(1, 0, 0);
				if (v.y() < 0.0f)
					angleH = PI2 - angleH;
				BReal angleV = v.angle(0, 0, 1);
				cs[c].set(float(angleH / PI2), float(angleV / PI));
			}
		}
	}

	BVectorArray* normals = new BVectorArray(*vertices);
	for (int i = 0; i < vertices->size(); i++)
	{
		(*vertices)[i] *= radius;
	}

	int faceStride = segment * segment;
	BFaceArray* faces = new BFaceArray(faceStride * 20);
	BFace* fs = faces->data();
	int fi = 0;
	for (int r = 1; r <= segment; r++)
	{
		int head0 = (r - 1) * r / 2;
		int head1 = (r + 1) * r / 2;
		int cols = r - 1;
		for (int c = 0; c < cols; c++)
		{
			int a0 = head0 + c;
			int a1 = a0 + 1;
			int b0 = head1 + c;
			int b1 = b0 + 1;
			fs[fi++].set(a0, b0, b1);
			fs[fi++].set(b1, a1, a0);
		}
		int a0 = head0 + r - 1;
		int b0 = head1 + r - 1;
		int b1 = b0 + 1;
		fs[fi++].set(a0, b0, b1);
	}
	for (int i = 1; i < 20; i++)
	{
		int vertexBegin = stride * i;
		for (int j = 0; j < faceStride; j++)
		{
			fs[fi++] = fs[j] + vertexBegin;
		}
	}
	boss->setVertices(vertices);
	boss->setNormals(normals);
	boss->setTexCoords(coords);
	boss->setFaces(faces);

	return true;
}
